package cn.activiti.demo.dimission.service.impl;

import cn.activiti.demo.dimission.model.Note;
import cn.activiti.demo.dimission.model.ProcessElement;
import cn.activiti.demo.dimission.service.ExpenseAccountActivitiService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricDetail;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.HistoricActivityInstanceEntityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;

import javax.servlet.http.HttpUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

@Service
public class ExpenseAccountActivitiServiceImpl implements ExpenseAccountActivitiService {

    private final String ProcessID = "expenseAccountProcess";

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ProcessEngine processEngine;

    @Override
    public String deployProcess() {
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("processes/expense-account-process.bpmn20.xml")
                .deploy();
        return null;
    }

    @Override
    public void startProcessByKey(String key) {

    }

    @Override
    public ProcessInstance editAndSave(Note note) {
        note.setId(UUID.randomUUID().toString());
        Map variables = JSON.parseObject(JSON.toJSONString(note), Map.class);
/**
 * 开启一个流程
 */
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(ProcessID,note.getId(),variables);
//        runtimeService.suspendProcessInstanceById(processInstance.getId());
//        runtimeService.deleteProcessInstance(processInstance.getId(), "删除流程实例的原因");
//        taskService.addCandidateGroup(task.getId(), "指定审核候选人");
        return processInstance;
    }

    @Override
    public void submit(String business) {
//        通过业务key查询Task
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(business).singleResult();
//        通过流程实例ID查询Task
//        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
/**
 * 在某些流程的点上可能会重复触发，这个时候需要判断一下是不是在确认的那个待办点
 */
        if (!task.getTaskDefinitionKey().equals("submit")) {
            return;
        }
        System.out.println(task.getId()+","+task.getName());

//        提交审核
        taskService.complete(task.getId());
    }

    @Override
    public void auditReject(String businessKey, String rejectedReason) {

        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
//        审核拒绝
        Map<String, Object> variables = new LinkedHashMap<>();
        variables.put("audit", "reject");
        variables.put("rejectedReason", rejectedReason);

        taskService.addComment(task.getId(), task.getProcessInstanceId(), "text", rejectedReason);
        taskService.addCandidateGroup(task.getId(), "指定审核候选部门");
        taskService.addCandidateUser(task.getId(), "指定审核候选人");
        taskService.setAssignee(task.getId(), "受理人");

//        task.setAssignee();

        taskService.complete(task.getId(), variables);
    }

    @Override
    public void auditApprove(String businessKey) {
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();
//        审核通过
        Map<String, Object> variables = new LinkedHashMap<>();
        variables.put("audit", "approve");

        taskService.setAssignee(task.getId(), "部门领导Manager");

        taskService.complete(task.getId(), variables);
    }

    @Override
    public void settlement(String businessKey) {
        Task task = taskService.createTaskQuery().processInstanceBusinessKey(businessKey).singleResult();

        taskService.setAssignee(task.getId(), "单位会计");
        taskService.addComment(task.getId(), task.getProcessInstanceId(),   "银行卡转账：500 RMB");
        System.out.println(task.getId()+","+task.getName());

        taskService.complete(task.getId());
    }

    @Override
    public JSONArray watchProcess(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        JSONArray jsonArray = new JSONArray();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            System.out.println(JSON.toJSONString(historicActivityInstance));
            jsonArray.add(JSON.toJSONString(historicActivityInstance));
        }

        return jsonArray;
    }

    @Override
    public List<ProcessElement> watchProcessWithNextStep(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        List<ProcessElement> processElementList = new LinkedList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            ProcessElement element = new ProcessElement();
            element.setStartTime(historicActivityInstance.getStartTime());
            element.setEndTime(historicActivityInstance.getEndTime());
            element.setId(historicActivityInstance.getActivityId());
            element.setAssignee(historicActivityInstance.getAssignee());
            element.setName(historicActivityInstance.getActivityName());
            if (historicActivityInstance.getTaskId() != null) {
                Task task = taskService.createTaskQuery().taskId(historicActivityInstance.getTaskId()).singleResult();
                if (task != null) {
                    List<Comment> commentList = taskService.getTaskComments(task.getId());
                    if (commentList != null && commentList.size() > 0) {
                        element.setComment(commentList.get(0).getFullMessage());
                    }
                }
            }

            processElementList.add(element);
        }
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (task != null) {
            UserTask userTask = getNextTaskNode(task);
            ProcessElement element = new ProcessElement();

            element.setId(userTask.getId());
            element.setName(userTask.getName());

            processElementList.add(element);
        }

        return processElementList;
    }

    public List<UserTask> listProcessTask(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();
        BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(historicActivityInstanceList.get(0).getProcessDefinitionId());
        List<Process> processList = bpmnModel.getProcesses();
        List<UserTask> nodeList = new LinkedList<>();

        if (processList != null && processList.size() > 0) {
            Process process = processList.get(0);
            Map<String, FlowElement> flowElementMap = process.getFlowElementMap();
            if (flowElementMap != null) {
                for (FlowElement flowElement : flowElementMap.values()) {
                    if (flowElement != null && flowElement instanceof UserTask ) {
                        UserTask task = (UserTask) flowElement;
                        nodeList.add(task);
                    } else if (flowElement != null && flowElement instanceof EndEvent ) {
                        UserTask userTask = new UserTask();
                        userTask.setId("9999");
                        userTask.setName("endEvent");
                        nodeList.add(userTask);
                    } else if (flowElement != null && flowElement instanceof StartEvent ) {

                        UserTask userTask = new UserTask();
                        userTask.setId("1");
                        userTask.setName("startEvent");
                        nodeList.add(userTask);
                    }
                }
            }
        }

        return nodeList;
    }

    public JSONArray watchProcessWithNextStepJSON(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(historicActivityInstanceList.get(0).getProcessDefinitionId());
//        List<Process> processList = bpmnModel.getProcesses();

        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        JSONArray jsonArray = new JSONArray();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            jsonArray.add(JSON.toJSONString(historicActivityInstance));
        }

        JSONObject nextTask = new JSONObject();

        nextTask.put("id", task.getId());
        nextTask.put("name", task.getName());

        jsonArray.add(nextTask);

//        historicActivityInstanceList.add(processActivityInstanceList.get(historicActivityInstanceList.size()));

        return jsonArray;
    }

    public JSONArray watchProcessWithNextStepJSON2(String processInstanceId) {
        List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId).list();

        BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(historicActivityInstanceList.get(0).getProcessDefinitionId());
        List<Process> processList = bpmnModel.getProcesses();

//        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        for (Process process : processList) {

        }

        JSONArray jsonArray = new JSONArray();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstanceList) {
            jsonArray.add(JSON.toJSONString(historicActivityInstance));
        }

        JSONObject nextTask = new JSONObject();

//        nextTask.put("id", task.getId());
//        nextTask.put("name", task.getName());

        jsonArray.add(nextTask);

//        historicActivityInstanceList.add(processActivityInstanceList.get(historicActivityInstanceList.size()));

        return jsonArray;
    }

    public void watchProcessImage(OutputStream outputStream) {
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey(ProcessID).list();

        BpmnModel bpmnModel = repositoryService.getBpmnModel(taskList.get(0).getProcessDefinitionId());

        // ID 为 流程定义Key
        List listsss = bpmnModel.getProcesses();
        Process process = bpmnModel.getProcesses().get(0);

        // 流程节点ID
        FlowElement flowElement = process.getFlowElement(taskList.get(0).getTaskDefinitionKey());

        DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();

        List<String> highLightedActivities = new ArrayList<>();
        highLightedActivities.add(flowElement.getId());

//        InputStream inputStream = generator.generateDiagram(bpmnModel, "png", highLightedActivities, Collections.emptyList(), "宋体", "宋体", "宋体", null, 2.0);
        InputStream inputStream = generator.generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), "宋体", "宋体", "宋体", null, 2.0);

        try {
            FileCopyUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void watchProcessActivitiedImage(OutputStream outputStream, String processInstanceId) {
        try {
            HistoricProcessInstance historicProcessInstance = processEngine.getHistoryService()
                    .createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();

            List<HistoricActivityInstance> historicActivityInstanceList = processEngine.getHistoryService()
                    .createHistoricActivityInstanceQuery()
                    .processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceId()
                    .asc()
                    .list();

            BpmnModel bpmnModel = processEngine.getRepositoryService().getBpmnModel(historicProcessInstance.getProcessDefinitionId());

            List<Process> processList = bpmnModel.getProcesses();

            if (processList != null && processList.size() > 0) {
                Process process = processList.get(0);
                Map<String, FlowElement> flowElementMap = process.getFlowElementMap();
                if (flowElementMap != null) {
                    for (FlowElement flowElement : flowElementMap.values()) {
                        if (flowElement != null && flowElement instanceof SequenceFlow) {
                            String condition = ((SequenceFlow) flowElement).getConditionExpression();
                            flowElement.setDocumentation(getFlowElementName(condition));
                        }
                    }
                }
            }

            // 获取已经流转的线
            List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);

            // 构造已执行的流程节点ID集合
            List<String> executedActivityIds = new ArrayList<String>();
            for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
                executedActivityIds.add(activityInstance.getActivityId());
            }

            //获取流程图信息
            DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
            InputStream inputStream = generator.generateDiagram(bpmnModel, "png", executedActivityIds, flowIds, "Songti", "Songti", "Songti", null, 2.0);

            try {
                FileCopyUtils.copy(inputStream, outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public JSONObject getNextTask(String processInstanceId) {

        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        UserTask userTask = getNextTaskNode(task);

        List<String> candidateGroups = userTask.getCandidateGroups();
        String name = userTask.getName();
        String id = userTask.getId();

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("candidate", candidateGroups);
        jsonObject.put("name", name);
        jsonObject.put("id", id);

        return jsonObject;
    }

    private List<String> getExecutedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstanceList) {
        List<String> executedFlowIdList = new ArrayList<>();
        for (int i = 0; i < historicActivityInstanceList.size() - 1; i++) {
            HistoricActivityInstance hai = historicActivityInstanceList.get(i);
            FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(hai.getActivityId());
            List<SequenceFlow> sequenceFlows = flowNode.getOutgoingFlows();
            if (sequenceFlows.size() > 1) {
                HistoricActivityInstance nextHai = historicActivityInstanceList.get(i + 1);
                sequenceFlows.forEach(sequenceFlow -> {
                    if (sequenceFlow.getTargetFlowElement().getId().equals(nextHai.getActivityId())) {
                        executedFlowIdList.add(sequenceFlow.getId());
                    }
                });
            } else if (sequenceFlows.size() == 1) {
                executedFlowIdList.add(sequenceFlows.get(0).getId());
            }
        }
        return executedFlowIdList;
    }

    private String getFlowElementName(String condition) {
        if (org.springframework.util.StringUtils.isEmpty(condition)) {
            return "";
        }
        condition = StringUtils.substringBetween(condition, "'");

        return condition;
    }

    public UserTask getNextTaskNode(Task task) {
        FlowElement resultTask = null;//定义返回值
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());//通过流程定义ID取到bomnModel对象
        FlowElement flowElement = bpmnModel.getProcesses().get(0).getFlowElement(task.getTaskDefinitionKey());//直接使用当前task的定义key去查找模型中的节点(当前任务节点)
        UserTask nowUserTask = (UserTask)flowElement;//获取当前UserTask
        List<SequenceFlow> nowOutgoingFlows = nowUserTask.getOutgoingFlows();//当前任务流向节点(SequenceFlow: 连接两个节点之间的线)
        if( nowOutgoingFlows != null && nowOutgoingFlows.size() == 1 ) {//如果只流向一个节点
            FlowElement nextFlowElement = nowOutgoingFlows.get(0).getTargetFlowElement();//下一节点
            if(UserTask.class.isInstance(nextFlowElement)) {//判断节点是否是UserTask
                resultTask = nextFlowElement;
            }else if(ExclusiveGateway.class.isInstance(nextFlowElement)){//判断是否是排他网关
                ExclusiveGateway exclusiveGateway = (ExclusiveGateway)nextFlowElement;
                List<SequenceFlow> exclusiveOutgoingFlows = exclusiveGateway.getOutgoingFlows();//排他网关流向节点
                if( exclusiveOutgoingFlows != null && exclusiveOutgoingFlows.size() == 1 && (UserTask.class.isInstance(exclusiveOutgoingFlows.get(0).getTargetFlowElement())|| EndEvent.class.isInstance(exclusiveOutgoingFlows.get(0).getTargetFlowElement()))) {//如果排他网关只有一条线路信息并且是UserTask节点
                    resultTask = exclusiveOutgoingFlows.get(0).getTargetFlowElement();
                }else if(exclusiveOutgoingFlows.size() > 1) {//如果排他网关有多条线路信息
                    for (SequenceFlow sequenceFlow : exclusiveOutgoingFlows) {

                        if(sequenceFlow.getDocumentation().indexOf("$默认$") >= 0 || sequenceFlow.getDocumentation().indexOf("approve") >= 0) {//判断跳转条件是否成立
                            resultTask = sequenceFlow.getTargetFlowElement();//条件成立  返回节点对象
                        }
                    }
                }
            } else if (EndEvent.class.isInstance(nextFlowElement)) {
                UserTask userTask = new UserTask();
                userTask.setName("endEvent");
                userTask.setId("endEvent");
                userTask.setDocumentation(nextFlowElement.getDocumentation());

                resultTask = userTask;
            }
        }
        return (UserTask) resultTask;
    }

}
